﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lucandra.Net
{
    internal sealed class ByteBuffer
    {
        private byte[] buffer;
        int position;
        bool isWrite;

        public ByteBuffer(byte[] bytes)
        {
            this.buffer = bytes;
            this.position = 0;
            this.isWrite = false;
        }

        public ByteBuffer(int initialSize)
        {
            this.buffer = new byte[initialSize];
            this.position = 0;
            this.isWrite = true;
        }

        public byte ReadByte()
        {
            if (this.isWrite)
                throw new InvalidOperationException("This ByteBuffer was opened in write-mode.");

            this.position++;
            return this.buffer[this.position - 1];
        }

        public int ReadVariableInt32()
        {
            if (this.isWrite)
                throw new InvalidOperationException("This ByteBuffer was opened in write-mode.");

            int length = this.buffer.Length;

            if (length == 0)
                return 0;

            byte b = this.buffer[this.position];
            this.position++;
            int i = b & 0x7F;
            for (int shift = 7; (b & 0x80) != 0 && this.position < length; shift += 7, this.position++)
            {
                b = this.buffer[this.position];
                i |= (b & 0x7F) << shift;
            }

            return i;
        }

        public int ReadInt32()
        {
            if (this.isWrite)
                throw new InvalidOperationException("This ByteBuffer was opened in write-mode.");

            int ret = BitConverter.ToInt32(this.buffer, this.position);
            this.position += 4;
            return ret;
        }

        public void WriteByte(byte b)
        {
            if (!this.isWrite)
                throw new InvalidOperationException("This ByteBuffer was opened in read-mode.");

            this.AssertLengthForWrite(1);

            this.buffer[this.position] = b;
            this.position++;
        }

        public void WriteVariableInt32(int i)
        {
            if (!this.isWrite)
                throw new InvalidOperationException("This ByteBuffer was opened in read-mode.");

            int length = 0;
            int p = i;

            while ((p & ~0x7F) != 0)
            {
                p >>= 7;
                length++;
            }
            length++;

            byte[] buf = new byte[length];
            int pos = 0;
            while ((i & ~0x7F) != 0)
            {
                buf[pos] = ((byte)((i & 0x7f) | 0x80));
                i >>= 7;
                pos++;
            }
            buf[pos] = (byte)i;

            this.AssertLengthForWrite(buf.Length);
            Array.Copy(buf, 0, this.buffer, this.position, buf.Length);
            this.position += buf.Length;
        }

        public byte[] GetBytes()
        {
            if (this.isWrite)
            {
                var ret = new byte[this.position];
                Array.Copy(this.buffer, ret, ret.Length);
                return ret;
            }
            else
                return this.buffer;
        }

        private void AssertLengthForWrite(int length)
        {
            if (this.position + length > this.buffer.Length)
            {
                var tmp = this.buffer;
                this.buffer = new byte[this.buffer.Length + 64 + length];
                Array.Copy(tmp, this.buffer, tmp.Length);
            }
        }
    }
}
